/* EasyWay Game Engine
 * Copyright (C) 2006 Daniele Paggi.
 *  
 * Written by: 2006 Daniele Paggi<dshnt@hotmail.com>
 *   
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.easyway.objects.texture;

import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import org.easyway.interfaces.base.IDestroyable;
import org.easyway.system.state.OpenGLState;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.glu.GLU;

public class TextureID implements IDestroyable, Serializable {

	private static final long serialVersionUID = 4409253583065563738L;

	protected ImageData data;

	protected int width;

	protected int height;

	protected int widthHW;

	protected int heightHW;

	protected int format;

	protected boolean solid;

	transient protected IntBuffer idBuffer;

	transient protected int id;

	public static boolean USE_MIPMAP = true;

	public static boolean FREE_MEMORY = true;

	int referenceCount = 0;
	
	/** 
	 * the transparent Component of the image
	 */
	protected byte alphaRed, alphaGreen, alphaBlue;
	
	/**
	 * the filename from where I've loaded the TextureID
	 */
	protected String pathFileName;

	protected TextureID() {
	}

	public TextureID(int width, int height) {
		this(width, height, true);
	}

	public TextureID(int width, int height, boolean useAlpha) {
		data = new ImageData(width, height, useAlpha);
		createTexture();
	}

	public TextureID(ImageData image) {
		data = image;
		createTexture();
	}

	public TextureID(String path) {
		this(path, true);
	}

	public TextureID(String path, int red, int green, int blue) {
		(data = new ImageData(path, true)).makeTransp((byte) red, (byte) green,
			(byte) blue);
		alphaRed = (byte)red;
		alphaGreen = (byte)green;
		alphaBlue = (byte)blue;
		pathFileName = path;
		createTexture();
	}

	public TextureID(String path, boolean useAlpha) {
		data = new ImageData(path, useAlpha);
		pathFileName = path;
		createTexture();
	}

	private void createTexture() {
		width = data.width;
		height = data.height;
		widthHW = data.widthHW;
		heightHW = data.heightHW;
		format = data.format;
		solid = data.solid;
		// generate the ID
		idBuffer = BufferUtils.createIntBuffer(4);
		GL11.glGenTextures(idBuffer);
		id = idBuffer.get(0);
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, OpenGLState.lastTextureID = id);
		// Filtering
		if (USE_MIPMAP) {
			GL11.glTexParameteri(GL11.GL_TEXTURE_2D,
				GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR);
		} else {
			GL11.glTexParameteri(GL11.GL_TEXTURE_2D,
				GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
		}
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER,
			GL11.GL_LINEAR);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S,
			GL12.GL_CLAMP_TO_EDGE);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T,
			GL12.GL_CLAMP_TO_EDGE);
		// Generate The Texture - NEW
		if (USE_MIPMAP) {
			GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, format, widthHW,
				heightHW, format, GL11.GL_UNSIGNED_BYTE, data.data);
		} else {
			GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, format, widthHW, heightHW,
				0, format, GL11.GL_UNSIGNED_BYTE, data.data);
		}
		if (FREE_MEMORY) {
			data.data = null;
			data = null;
		}
	}

	@Deprecated public TextureID(TextureID source) {
		// TODO
	}

	// ------------------------------------------------------------
	// ---------------------------USERS----------------------------
	// ------------------------------------------------------------

	/**
	 * binds the current texture
	 */
	public void bind() {
		if (id != OpenGLState.lastTextureID) {
			GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
			OpenGLState.lastTextureID = id;
		}
	}

	/**
	 * free memory space
	 * 
	 */
	public void freeMemory() {
		// removed for speedup GC
		// if (data != null)
		// data.data = null;
		data = null;
	}

	// ------------------------------------------------------------
	// ---------------------------DATA-----------------------------
	// ------------------------------------------------------------

	/**
	 * if the data is null will be free the memory from the RAM but not from the
	 * VRAM
	 */
	public void setData(ByteBuffer bdata) {
		if (bdata == null) {
			freeMemory();
			return;
		}
		bind();
		bdata.rewind();
		if (format == ImageType.RGBA) {
			GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, widthHW,
				heightHW, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, bdata);
			if (data != null)
				data.data = bdata;
		} else {
			assert format == ImageType.RGB; // RGB
			GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, widthHW,
				heightHW, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, bdata);
			if (data != null)
				data.data = bdata;
		}
	}

	public ByteBuffer getData() {
		if (data != null && data.data != null) {
			return data.data;
		}
		bind();
		if (format == ImageType.RGB) {
			ByteBuffer bb = ByteBuffer.allocateDirect(heightHW * widthHW * 3);
			GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB,
				GL11.GL_UNSIGNED_BYTE, bb);
			return bb;
		} else { // RGBA
			assert format == ImageType.RGBA;
			ByteBuffer bb = ByteBuffer.allocateDirect(heightHW * widthHW * 4);
			GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA,
				GL11.GL_UNSIGNED_BYTE, bb);
			return bb;
		}
	}

	// ------------------------------------------------------------
	// --------------------------DESTROY---------------------------
	// ------------------------------------------------------------

	public void destroy() {
		if (id == -1)
			return;
		idBuffer.rewind();
		GL11.glDeleteTextures(idBuffer);
		// idBuffer = null;
		id = -1;
	}

	public void finalize() {
		if (id == -1)
			return;
		idBuffer.rewind();
		GL11.glDeleteTextures(idBuffer);
		id = -1;
		// removed for speed up the GC
		// idBuffer = null;

	}

	public boolean isDestroyed() {
		return id == -1;
	}

	// ------------------------------------- SERIALIZATION -----------

	private void writeObject(java.io.ObjectOutputStream s)
			throws java.io.IOException {
		if (data == null) {
			data = new ImageData(this, getData());
		}
		s.defaultWriteObject();
	}

	private void readObject(java.io.ObjectInputStream s)
			throws java.io.IOException, ClassNotFoundException {
		s.defaultReadObject();
		createTexture();
	}

	public boolean isSolid() {
		return solid;
	}

	public byte getAlphaRed() {
		return alphaRed;
	}

	public byte getAlphaGreen() {
		return alphaGreen;
	}

	public byte getAlphaBlue() {
		return alphaBlue;
	}

	public String getPathFileName() {
		return pathFileName;
	}
}
